from webutils.layers.services import ManagerProxy

from .. import const, managers
from .base import BaseService


class UserService(BaseService):
    required_context = ['room_id', 'group_enum_class']
    user_counter = ManagerProxy(managers.GroupCounter)

    def __init__(self, *args, group_enum_class=const.UserGroup, **kwargs):
        super().__init__(*args, group_enum_class=group_enum_class, **kwargs)

    @staticmethod
    def get_enum_int_map(enum_cls):
        """
        Support we has an enum_cls bellow
            class Group(enum.Enum):
                TEACHER = 'T'
                STUDENT = 'S'

        With this method we get
            {Group.TEACHER: 0, Group.STUDENT: 1}
        """
        lst = [item for item in enum_cls]
        lst.sort(key=lambda item: item.name)
        mapping = {item: index for index, item in enumerate(lst)}
        return mapping

    def get_group_value(self, group):
        group_int_map = self.get_enum_int_map(self.group_enum_class)
        return group_int_map[group]

    def get_group_key(self, int_value):
        group_int_map = self.get_enum_int_map(self.group_enum_class)
        int_group_map = {value: key for key, value in group_int_map.items()}
        return int_group_map[int(int_value)]

    async def login_user(self, user_id, group, session_data):
        msg = f'group {group} is expected as {self.group_enum_class}'
        assert isinstance(group, self.group_enum_class), msg

        stage = managers.UserOnlineStage(self, user_id=user_id)
        user_info = managers.UserInfo(self, user_id=user_id)
        await stage.set(const.OnlineStage.REGISTERED)
        await user_info.set(session_data)
        group_value = self.get_group_value(group)
        await self.user_counter.add(group_value, user_id)

    async def heartbeat(self, user_id):
        stage = managers.UserOnlineStage(self, user_id=user_id)
        await stage.refresh()

    async def logout_user(self, user_id):
        stage = managers.UserOnlineStage(self, user_id=user_id)
        await stage.set(const.OnlineStage.CLOSED)
        await self.user_counter.pop(user_id)

    async def get_users(self, filter_kwargs=None):
        filter_kwargs = filter_kwargs or {}
        group_users = await self.user_counter.all()
        group_users = {
            self.get_group_key(key): value
            for key, value in group_users.items()
        }
        for role, user_id_lst in group_users.items():
            user_info_list = []
            for index, user_id in enumerate(user_id_lst):
                user_info = managers.UserInfo(self, user_id=user_id)
                session_data = await user_info.get()
                if not session_data:
                    continue
                # 对参数进行筛选
                for key, value in filter_kwargs.items():
                    if session_data.get(key) != value:
                        continue
                user_info_list.append(session_data)
            group_users[role] = user_info_list
        return group_users

    async def get_user_group(self, user_id):
        score = await self.user_counter.get_score(user_id)
        if score is None:
            return self.group_enum_class.GUEST
        return self.get_group_key(score)

    async def get_user_info(self, user_id):
        user_info_manager = managers.UserInfo(self, user_id=user_id)
        return await user_info_manager.get()


class ProxyUserService(BaseService):
    required_context = ['room_id', 'proxy_id']
    user_counter = ManagerProxy(managers.ProxyUserCounter)

    async def add_user(self, user_id):
        await self.user_counter.add(user_id)

    async def clear(self):
        await self.user_counter.clear()

    async def get_users(self):
        return await self.user_counter.all()
